#include "good_vector.h"
#include <algorithm>

good_vector::good_vector()
{
   size_ = 0;
   capacity_ = 1;
}

good_vector::~good_vector()
{
   if (size_ > 1)
   {
      delete[] elements_.digits;
   }
}

good_vector::good_vector(size_t length, size_t fill)
{
   size_ = length;
   if (length == 1)
   {
      capacity_ = 1;
      elements_.digit = fill;
   }
   else
   {
      capacity_ = expand_ * length;
      elements_.digits = new size_t[capacity_];
      for (size_t i = 0; i < size_; i++)
      {
         elements_.digits[i] = fill;
      }
   }
}

good_vector::good_vector(const good_vector& v)
{
   size_ = v.size_;
   capacity_ = v.capacity_;
   if (size_ > 1)
   {
      elements_.digits = new size_t[capacity_];
      copy(elements_.digits, v.elements_.digits, size_);
   }
   else
   {
      elements_.digit = v.elements_.digit;
   }
}

void good_vector::copy(size_t* to, const size_t* from, size_t length)
{
   for (int i = 0; i < length; i++)
   {
      to[i] = from[i];
   }
}

void good_vector::expand()
{
   capacity_ *= expand_;
   size_t* new_digits = new size_t[capacity_];
   if (size_ > 1)
   {
      copy(new_digits, elements_.digits, size_);
      delete[] elements_.digits;
   }
   else
   {
      new_digits[0] = elements_.digit;
   }
   elements_.digits = new_digits;
}

void good_vector::collapse()
{
   if (capacity_ == 1)
   {
      return;
   }
   capacity_ /= expand_;
   if (size_ == 1)
   {
      size_t t = elements_.digits[0];
      elements_.digit = t;
      capacity_ = 1;
      return;
   }
   size_t* new_digits = new size_t[capacity_];
   copy(new_digits, elements_.digits, size_); 
   delete[] elements_.digits;
   elements_.digits = new_digits;
}

void good_vector::ensure_capacity(size_t new_capacity_)
{
   if (new_capacity_ > std::max(capacity_, static_cast<size_t>(1)))
   {
      expand();
   }
   else 
   {   
      if ((new_capacity_ * collapse_ < capacity_) || (size_ == 1))
      {
         collapse();
      }
   }
}

void good_vector::push_back(size_t val)
{
   ensure_capacity(size_ + 1);
   (*this)[size_++] = val;        
}

size_t good_vector::pop_back()
{
   size_t tmp = (*this)[--size_];
   ensure_capacity(size_);
   return tmp;
}

size_t good_vector::size() const
{
   return size_;
}

void good_vector::resize(size_t new_size)
{        
   if (new_size == 1)
   {
      size_t t = (*this)[0];
      if (capacity_ > 1)
      {
         delete[] elements_.digits;
      }
      elements_.digit = t;
      size_ = capacity_ = 1;
      return;
   }

   size_t* t = new size_t[new_size];
   for (int i = 0; i < std::min(size_, new_size); i++)
   {
      t[i] = (*this)[i];
   }
   if (capacity_ > 1)
   {
      delete[] elements_.digits;
   }
   elements_.digits = t;
   size_ = capacity_ = new_size;
}

void good_vector::reverse()
{
   if (size_ == 1) {
      return;
   }
   for (size_t i = 0; i < size_ / 2; i++)
   {
      size_t tmp = elements_.digits[i];
      elements_.digits[i] = elements_.digits[size_ - i - 1];
      elements_.digits[size_ - i - 1] = tmp;
   }
}

void good_vector::swap(good_vector& other)
{
   std::swap(size_, other.size_);
   std::swap(capacity_, other.capacity_);
   std::swap(elements_.digit, other.elements_.digit);
}


good_vector& good_vector::operator =(const good_vector& v)
{
   if (this == &v)
   {
      return *this;
   }
   if (size_ > 1)
   {
      delete[] elements_.digits;
   }
   size_ = v.size_;
   capacity_ = v.capacity_;
   if (size_ == 1)
   {
      elements_.digit = v.elements_.digit;
   }
   else
   {
      elements_.digits = new size_t[capacity_];
      copy(elements_.digits, v.elements_.digits, size_);
   }
   return *this;
}

size_t good_vector::operator [](size_t index) const
{
   if (size_ == 1)
   {
      return this->elements_.digit;
   }
   else
   {
      return (this->elements_.digits)[index];
   }
}

size_t& good_vector::operator [](size_t index)
{
   if (capacity_ == 1)
   {
      return this->elements_.digit;
   }
   else
   {
      return (this->elements_.digits)[index];
   }
}